﻿using Nancy.Json;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Utils_Base;

namespace Utils_Cache
{
    public class RedisHelper1
    {
        IDatabase _db = null;
        //public RedisHelper()
        //{
        //    UseStackExchangeRedis();
        //}
        static ConfigurationOptions connDCS = ConfigurationOptions.Parse(UtilAppsettingCache.getSetting("redis:host") + ",password=" + UtilAppsettingCache.getSetting("redis:password") + ", connectTimeout=2000");

        static IConnectionMultiplexer connect;
        /// <summary>
        /// 实始化
        /// </summary>
        // StackExchange.Reids连接Redis-Sentinels
        public void UseStackExchangeRedis()
        {

            if (connect != null)
            {
                _db = connect.GetDatabase(0);
                return;
            }

            string redisHost = UtilAppsettingCache.getSetting("redis:host");// "43.139.58.147:6379";// Configini.ConfigModel.Redis_Host;
            string redisServiceName = UtilAppsettingCache.getSetting("redis:serviceName");// "redis-6379";// Configini.ConfigModel.Redis_ServiceName;
            string redisPassword = UtilAppsettingCache.getSetting("redis:password"); //"yihong712";// Configini.ConfigModel.Redis_Password;

            var config = new ConfigurationOptions
            {
                AbortOnConnectFail = false,
                //AllowAdmin = true,
                ConnectTimeout = 15000,
                SyncTimeout = 5000,
                Password = UtilAppsettingCache.getSetting("redis:password"),//Redis数据库密码
                EndPoints = { UtilAppsettingCache.getSetting("redis:host") }// connectionString 为IP:Port 如”192.168.2.110:6379”
            };
            connect = ConnectionMultiplexer.Connect(config);
            _db = connect.GetDatabase(0);

        }
        /// <summary>
        /// 设置 key 并保存字符串（如果 key 已存在，则覆盖值）
        /// </summary>
        /// <param name="redisKey">名称</param>
        /// <param name="redisValue">值</param>
        /// <param name="expiry">时间</param>
        /// <returns></returns>
        public bool StringSet(string redisKey, string redisValue, TimeSpan? expiry = null)
        {
            UseStackExchangeRedis();
            if (expiry == null)
                expiry = new TimeSpan(0, 30, 0);
            return _db.StringSet(redisKey, redisValue, expiry);
        }

        /// <summary>
        /// 获取字符串
        /// </summary>
        /// <param name="redisKey">名称</param>
        /// <param name="expiry">时间</param>
        /// <returns></returns>
        public string StringGet(string redisKey, TimeSpan? expiry = null)
        {
            UseStackExchangeRedis();
            return _db.StringGet(redisKey);
        }

        /// <summary>
        /// 存储一个对象（该对象会被序列化保存）
        /// </summary>
        /// <param name="redisKey">名称</param>
        /// <param name="redisValue">值</param>
        /// <param name="expiry">时间</param>
        /// <returns></returns>
        public bool StringSet<T>(string redisKey, T redisValue, int min = 30)
        {
            UseStackExchangeRedis();
            TimeSpan expiry = new TimeSpan(0, min, 0);
            var json = JsonConvert.SerializeObject(redisValue);// Serialize(redisValue);

            return _db.StringSet(redisKey, json, expiry);
        }

        /// <summary>
        /// 获取一个对象（会进行反序列化）
        /// </summary>
        /// <param name="redisKey">名称</param>
        /// <param name="expiry">时间</param>
        /// <returns></returns>
        public T StringGet<T>(string redisKey, TimeSpan? expiry = null)
        {
            UseStackExchangeRedis();
            string result = _db.StringGet(redisKey);
            if (string.IsNullOrWhiteSpace(result))
            {
                return default;
            }
            return new JavaScriptSerializer().Deserialize<T>(result);
            //return Deserialize<T>(_db.StringGet(redisKey));
        }

        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private byte[] Serialize(object obj)
        {
            if (obj == null)
                return null;

            var binaryFormatter = new BinaryFormatter();
            using (var memoryStream = new MemoryStream())
            {
                DataContractSerializer ser = new DataContractSerializer(typeof(object));
                ser.WriteObject(memoryStream, obj);
                var data = memoryStream.ToArray();
                return data;
            }
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        private T Deserialize<T>(byte[] data)
        {
            if (data == null)
                return default;

            var binaryFormatter = new BinaryFormatter();
            using (var memoryStream = new MemoryStream(data))
            {
                XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(memoryStream, new XmlDictionaryReaderQuotas());
                DataContractSerializer ser = new DataContractSerializer(typeof(T));
                var result = (T)ser.ReadObject(reader, true);
                return result;
            }
        }
        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="redisKey"></param>
        /// <returns></returns>
        public bool KeyDelete(string redisKey)
        {
            UseStackExchangeRedis();
            return _db.KeyDelete(redisKey);
        }
        /// <summary>
        /// 分布式redis锁
        /// </summary>
        /// <param name="key">锁的Key</param>
        /// <param name="lockExpirySec">锁的时长（单位：秒）</param>
        /// <param name="waitLockSec">等待加锁时间（单位：毫秒）</param>
        /// <returns>true：加锁成功、false：加锁失败</returns>
        public bool Lock(string key, int lockExpirySec = 60)
        {
            UseStackExchangeRedis();
            string token = "Lock";
            return _db.LockTake("RediseLock:Locl_" + key, token, new TimeSpan(0, 0, 0, lockExpirySec));
        }

        /// <summary>
        /// 删除redis锁锁 执行完代码以后调用释放锁
        /// </summary>
        /// <param name="key"></param>
        public void DelLock(string key)
        {
            UseStackExchangeRedis();
            string token = "Lock";
            if (Lock(key) == false)
            {
                _db.LockRelease("RediseLock:Locl_" + key, token);
            }
        }
    }

}
